package com.shanxia.codec.cmd;

import com.shanxia.codec.ProtocolDataBase;
import com.shanxia.codec.enums.DecodeEnum;
import lombok.Data;

import java.math.BigDecimal;
import java.util.Arrays;

import static com.shanxia.codec.CodeConstant.*;
import static com.shanxia.codec.Decoder.*;

/**
 * C -> S
 * 充电桩状态信息包上报
 * 充电桩定期发送此信息上报充电桩当前工作状态信息
 * TODO 电流电压文档上为0.1，换算完之后为603，和65，是否应为0.01？
 */
@Data
public class Cmd104 extends ProtocolDataBase {

    public static final int MIN_SIZE = 234;

    private int v1;                             // 预留1
    private int v2;                             // 预留2
    private String sn;                          // 充电桩编码
    private int pileCount;                      // 充电枪数量
    private int chargingPortNum;                // 充电口号
    private int pileType;                       // 充电枪类型
    private int workingStatus;                  // 工作状态
    private int curSOC;                         // 当前SOC%
    private int curMaxWarningCode;              // 当前最高告警编码
    private int carConnStatus;                  // 车连接状态
    private double curTotalChargingCost;        // 本次充电累计充电费用
    private int v3;                             // 内部变量2, 预留
    private int v4;                             // 内部变量3, 预留
    private double dcChargingVol;               // 直流充电电压
    private double dcChargingCurrent;           // 直流充电电流
    private double bmsNeedVol;                  // BMS需求电压
    private double bmsNeedCurrent;              // BMS需求电流
    private int bmsChargingMode;                // BMS充电模式
    private double acAChargingVol;              // 交流A相充电电压
    private double acBChargingVol;              // 交流B相充电电压
    private double acCChargingVol;              // 交流C相充电电压
    private double acAChargingCurrent;          // 交流A相充电电流
    private double acBChargingCurrent;          // 交流B相充电电流
    private double acCChargingCurrent;          // 交流C相充电电流
    private int remainingTime;                  // 剩余充电时间
    private int chargingDuration;               // 充电时长
    private double curChargingEnergy;           // 本次充电累计充电电量
    private double beforeChargingValule;        // 充电前电表读数
    private double curChargingValue;            // 当前电表读数
    private int startWay;                       // 充电启动方式
    private int chargingStrategy;               // 充电策略
    private double chargingStrategyParam;       // 充电策略参数
    private int orderSign;                      // 预约标志
    private String chargingOrOrderNumber;       // 充电/预约卡号
    private int orderOvertimeTime;              // 预约超时时间
    private String startTime;                   // 预约/开始充电开始时间
    private double beforeChargingBalance;       // 充电前卡余额
    private int v5;                             // 预留
    private double chargingPower;               // 充电功率
    private int v6;                             // 系统变量3, 预留
    private int v7;                             // 系统变量4, 预留
    private int v8;                             // 系统变量5, 预留
    private int ventTempe;                      // 出风口温度
    private int envTempe;                       // 环境温度
    private int pileTempe;                      // 充电枪温度
    private String vin;                         // 车辆VIN码
    private int cockpitStatus;                  // 舱门状态
    private int allowChargingPower;             // 充电用户是否允许限充电功率
    private String serialNumber;                // 充电流水号
    private double totalChargingCost;           // 本次充电累计充电电费
    private double totalServiceFee;             // 本次充电累计充电服务费
    private int homingStatus;                   // 枪归位状态
    private int lockStatus;                     // 地锁状态
    private double thousandthEnergy;            // 千分位电量

    public Cmd104(){
        super.setCmd(104);
    }

    public static Cmd104 decodeFromBase(ProtocolDataBase protocolDataBase) {
        Cmd104 cmd = new Cmd104();
        cmd.copyDataFrom(protocolDataBase);
        byte[] dataField = protocolDataBase.getDataField();
        //数据域长度
        if (dataField.length < MIN_SIZE) {
            cmd.setResult(DecodeEnum.InvalidData);
            return cmd;
        }
        int offset = 0;
        // 2字节预留1
        cmd.v1 = getUint16(dataField, offset);
        offset += 2;
        // 2字节预留2
        cmd.v2 = getUint16(dataField, offset);
        offset += 2;
        // 32字节充电桩编码
        cmd.sn = byteToAscii(Arrays.copyOfRange(dataField, offset, offset + 32), true);
        offset += 32;
        // 1字节充电桩数量
        cmd.pileCount = getUint8(dataField[offset++]);
        // 1字节充电口号
        cmd.chargingPortNum = getUint8(dataField[offset++]);
        // 1字节充电枪类型
        cmd.pileType = getBits(dataField[offset++], 0, 2);
        // 1字节工作状态
        cmd.workingStatus = getUint8(dataField[offset++]);
        // 1字节当前SOC%
        cmd.curSOC = getUint8(dataField[offset++]);
        // 4字节最高警告编码
        cmd.curMaxWarningCode = getInt32(dataField, offset);
        offset += 4;
        // 1字节车连接状态
        cmd.carConnStatus = getUint8(dataField[offset++]);
        // 4字节本次充电累计充电费用
        cmd.curTotalChargingCost = new BigDecimal(getInt32(dataField, offset)).multiply(PERCENT).doubleValue();
        offset += 4;
        // 4字节内部变量2
        cmd.v3 = getInt32(dataField, offset);
        offset += 4;
        // 4字节内部变量3
        cmd.v4 = getInt32(dataField, offset);
        offset += 4;
        // 2字节直流充电电压,充电有效（直流有效，交流置 0）
        cmd.dcChargingVol = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 2字节直流充电电流,充电有效（直流有效，交流置 0）
        cmd.dcChargingCurrent = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 2字节BMS需求电压,充电有效（直流有效，交流置 0）
        cmd.bmsNeedVol = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 2字节BMS需求电流,充电有效（直流有效，交流置 0）
        cmd.bmsNeedCurrent = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 1字节BMS充电模式,充电有效（直流有效，交流置 0）
        cmd.bmsChargingMode = getUint8(dataField[offset++]);
        // 2字节交流A相充电电压
        cmd.acAChargingVol = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 2字节交流B相充电电压
        cmd.acBChargingVol = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 2字节交流C相充电电压
        cmd.acCChargingVol = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 2字节交流A相充电电流
        cmd.acAChargingCurrent = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 2字节交流B相充电电流
        cmd.acBChargingCurrent = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 2字节交流C相充电电流
        cmd.acCChargingCurrent = new BigDecimal(getUint16(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 2;
        // 2字节剩余充电时间(min)
        cmd.remainingTime = getUint16(dataField, offset);
        offset += 2;
        // 4字节充电时长(秒)
        cmd.chargingDuration = getInt32(dataField, offset);
        offset += 4;
        // 4字节本次充电累计充电电量
        cmd.curChargingEnergy = new BigDecimal(getInt32(dataField, offset)).multiply(PERCENT).doubleValue();
        offset += 4;
        // 4字节充电前电表读数
        cmd.beforeChargingValule = new BigDecimal(getInt32(dataField, offset)).multiply(PERCENT).doubleValue();
        offset += 4;
        // 4字节充当前电表读数
        cmd.curChargingValue = new BigDecimal(getInt32(dataField, offset)).multiply(PERCENT).doubleValue();
        offset += 4;
        // 1字节充电启动方式
        cmd.startWay = getUint8(dataField[offset++]);
        // 1字节充电策略
        cmd.chargingStrategy = getUint8(dataField[offset++]);
        // 4字节充电策略参数
        cmd.chargingStrategyParam = getInt32(dataField, offset);
        if (cmd.chargingStrategy == 2 || cmd.chargingStrategy == 3) {
            cmd.chargingStrategyParam = new BigDecimal(String.valueOf(cmd.chargingStrategyParam)).multiply(PERCENT).doubleValue();
        }
        offset += 4;
        // 1字节预约标志
        cmd.orderSign = getUint8(dataField[offset++]);
        // 32字节充电/预约卡号
        cmd.chargingOrOrderNumber = byteToAscii(Arrays.copyOfRange(dataField, offset, offset + 32), true);
        offset += 32;
        // 1字节预约超时时间
        cmd.orderOvertimeTime = getUint8(dataField[offset++]);
        // 8字节预约/开始充电开始时间(7字节数据+1字节占位)
        cmd.startTime = getStandardTime(dataField, offset);
        offset += 8;
        // 4字节充电前卡余额
        cmd.beforeChargingBalance = new BigDecimal(getInt32(dataField, offset)).multiply(PERCENT).doubleValue();
        offset += 4;
        // 4字节预留
        cmd.v5 = getInt32(dataField, offset);
        offset += 4;
        // 4字节充电功率
        cmd.chargingPower = new BigDecimal(getInt32(dataField, offset)).multiply(TENTH).doubleValue();
        offset += 4;
        // 4字节系统变量3，预留
        cmd.v6 = getInt32(dataField, offset);
        offset += 4;
        // 4字节系统变量4，预留
        cmd.v7 = getInt32(dataField, offset);
        offset += 4;
        // 4字节系统变量5，预留
        cmd.v8 = getInt32(dataField, offset);
        offset += 4;
        // 1字节出风口温度
        cmd.ventTempe = getUint8(dataField[offset++]) + TEMPE_OFFSET;
        // 1字节环境温度
        cmd.envTempe = getUint8(dataField[offset++]) + TEMPE_OFFSET;
        // 1字节充电枪温度
        cmd.pileTempe = getUint8(dataField[offset++]) + TEMPE_OFFSET;
        // 18字节VIN码
        cmd.vin = byteToAscii(Arrays.copyOfRange(dataField, offset, offset + 18), true);
        offset += 18;
        // 1字节舱门状态
        // bit0：舱门状态
        cmd.cockpitStatus = getBits(dataField[offset], 0, 1);
        // bit1：是否允许限充电功率
        cmd.allowChargingPower = getBits(dataField[offset], 1, 1);
        offset++;
        // 32字节充电流水号
        cmd.serialNumber = byteToAscii(Arrays.copyOfRange(dataField, offset, offset + 32), true);
        offset += 32;
        // 4字节本次充电累计充电电费
        cmd.totalChargingCost = new BigDecimal(getInt32(dataField, offset)).multiply(PERCENT).doubleValue();
        offset += 4;
        // 4字节本次充电累计充电服务费
        cmd.totalServiceFee = new BigDecimal(getInt32(dataField, offset)).multiply(PERCENT).doubleValue();
        offset += 4;
        // 1字节枪归位状态
        cmd.homingStatus = getUint8(dataField[offset++]);
        // 1字节地锁状态
        cmd.lockStatus = getUint8(dataField[offset++]);
        // 1字节千分位电量
        cmd.thousandthEnergy = new BigDecimal(getUint8(dataField[offset++])).multiply(THOUSANDTH).doubleValue();
        return cmd;
    }

    @Override
    public String toString() {

        StringBuffer buffer = new StringBuffer();
        buffer.append("#cmd104#").append("\n");
        buffer.append("v1:").append(v1).append("\n");
        buffer.append("v2:").append(v2).append("\n");
        buffer.append("充电枪数量:").append(pileCount).append("\n");                      //
        buffer.append("充电口号:").append(chargingPortNum).append("\n");                //
        buffer.append("充电枪类型:").append(pileType).append("\n");                       //
        buffer.append("工作状态:").append(workingStatus).append("\n");                  //
        buffer.append("当前SOC%:").append(curSOC).append("\n");                         //
        buffer.append("当前最高告警编码:").append(curMaxWarningCode).append("\n");              //
        buffer.append("车连接状态:").append(carConnStatus).append("\n");                  //
        buffer.append("本次充电累计充电费用:").append(curTotalChargingCost).append("\n");        //
        buffer.append("内部变量2, 预留:").append(v3).append("\n");                             //
        buffer.append("内部变量3, 预留:").append(v4).append("\n");                             //
        buffer.append("直流充电电压:").append(dcChargingVol).append("\n");               //
        buffer.append("直流充电电流:").append(dcChargingCurrent).append("\n");           //
        buffer.append("BMS需求电压:").append(bmsNeedVol).append("\n");                  //
        buffer.append("BMS需求电流:").append(bmsNeedCurrent).append("\n");              //
        buffer.append("BMS充电模式:").append(bmsChargingMode).append("\n");                //
        buffer.append("交流A相充电电压:").append(acAChargingVol).append("\n");              //
        buffer.append("交流B相充电电压:").append(acBChargingVol).append("\n");              //
        buffer.append("交流C相充电电压:").append(acCChargingVol).append("\n");              //
        buffer.append("交流A相充电电流:").append(acAChargingCurrent).append("\n");          //
        buffer.append("交流B相充电电流:").append(acBChargingCurrent).append("\n");          //
        buffer.append("交流C相充电电流:").append(acCChargingCurrent).append("\n");          //
        buffer.append("剩余充电时间:").append(remainingTime).append("\n");                  //
        buffer.append("充电时长:").append(chargingDuration).append("\n");               //
        buffer.append("本次充电累计充电电量:").append(curChargingEnergy).append("\n");           //
        buffer.append("充电前电表读数:").append(beforeChargingValule).append("\n");        //
        buffer.append("当前电表读数:").append(curChargingValue).append("\n");            //
        buffer.append("充电启动方式:").append(startWay).append("\n");                       //
        buffer.append("充电策略:").append(chargingStrategy).append("\n");               //
        buffer.append("充电策略参数:").append(chargingStrategyParam).append("\n");       //
        buffer.append("预约标志:").append(orderSign).append("\n");                      //
        buffer.append("充电/预约卡号:").append(chargingOrOrderNumber).append("\n");       //
        buffer.append("预约超时时间:").append(orderOvertimeTime).append("\n");              //
        buffer.append("预约/开始充电开始时间:").append(startTime).append("\n");                   //
        buffer.append("充电前卡余额:").append(beforeChargingBalance).append("\n");       //
        buffer.append("预留:").append(v5).append("\n");                             //
        buffer.append("充电功率:").append(chargingPower).append("\n");               //
        buffer.append("系统变量3, 预留:").append(v6).append("\n");                             //
        buffer.append("系统变量4, 预留:").append(v7).append("\n");                             //
        buffer.append("系统变量5, 预留:").append(v8).append("\n");                             //
        buffer.append("出风口温度:").append(ventTempe).append("\n");                      //
        buffer.append("环境温度:").append(envTempe).append("\n");                       //
        buffer.append("充电枪温度:").append(pileTempe).append("\n");                      //
        buffer.append("车辆VIN码:").append(vin).append("\n");                         //
        buffer.append("舱门状态:").append(cockpitStatus).append("\n");                  //
        buffer.append("充电用户是否允许限充电功率:").append(allowChargingPower).append("\n");             //
        buffer.append("充电流水号:").append(serialNumber).append("\n");                //
        buffer.append("本次充电累计充电电费:").append(totalChargingCost).append("\n");           //
        buffer.append("本次充电累计充电服务费:").append(totalServiceFee).append("\n");             //
        buffer.append("枪归位状态:").append(homingStatus).append("\n");                   //
        buffer.append("地锁状态:").append(lockStatus).append("\n");                     //
        buffer.append("千分位电量:").append(thousandthEnergy).append("\n");
        return buffer.toString();
    }
}
